package cn.clx.ymcc.service.impl;

import cn.clx.ymcc.domain.AlipayInfo;
import cn.clx.ymcc.domain.PayOrder;
import cn.clx.ymcc.dto.AlipayNotifyDto;
import cn.clx.ymcc.dto.KillPreOrderParamsDto;
import cn.clx.ymcc.dto.PayNotifyDto;
import cn.clx.ymcc.dto.PayParametersDto;
import cn.clx.ymcc.exception.GlobalException;
import cn.clx.ymcc.service.IAlipayInfoService;
import cn.clx.ymcc.service.IPayOrderService;
import cn.clx.ymcc.service.IPayService;
import cn.clx.ymcc.util.AssertUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.common.models.AlipayTradeRefundResponse;
import com.alipay.easysdk.payment.page.Client;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.sun.corba.se.impl.ior.OldJIDLObjectKeyTemplate;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.Map;

@Service
@Slf4j
public class PayServiceImpl implements IPayService {

    @Autowired
    private IPayOrderService payOrderService;

    @Autowired
    private IAlipayInfoService alipayInfoService;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;
    /**
     * 访问支付宝进行支付
     * @param dto
     * @return
     */
    @Override
    public String apply(PayParametersDto dto) {
        //校验
        PayOrder payOrder = payOrderService.checkPayOrder(dto.getOrderNo());
        AssertUtil.isNotNull(payOrder,"支付单不存在");
        boolean OK = payOrder.getPayStatus() == PayOrder.STATE_WAIT_PAY;
        AssertUtil.isTrue(OK,"支付单状态异常");
        //根据不同的支付方式，访问不同的接口
        if(dto.getPayType()==1){    //支付宝付款
            return alipay(payOrder,dto);
        }else if (dto.getPayType()==2){ //银联付款
            return null;
        }
        return null;

    }

    /**
     * 支付宝异步通知支付结果
     * 返回的一个字符串，seccess 表示我们平台收到了支付宝的最大努力通知
     * 其他只要返回的不是 seccess 都表示我没有收到，或者说：收到的数据有问题（你发错了）
     * @param dto
     * @return
     */
    @Override
    public String asynNotify(AlipayNotifyDto dto) throws Exception{
        log.info("进入支付宝回调接口");
        //校验是否是支付宝发给我们的身份
        String jsonString = JSON.toJSONString(dto);
        Map<String,String> dtoMap = JSONObject.parseObject(jsonString, Map.class);
        //直接传入到一个方法中verifyNotify 进行校验身份
        Boolean isValid = Factory.Payment.Common().verifyNotify(dtoMap);
        if(!isValid){
            return "Fail";
        }
        //校验是否是我们的订单
        PayOrder payOrder = payOrderService.checkPayOrder(dto.getOut_trade_no());
        if(payOrder==null){
            return "Fail";
        }

        //如果支付单的支付状态为取消，支付宝回调又显示支付成功，则要进行退款
        if((payOrder.getPayStatus()==PayOrder.STATE_CANCEL)){
            try {
                log.info("调用支付宝退款！！");
                //调用退款
                AlipayTradeRefundResponse refund = Factory.Payment.Common()
                        .refund(payOrder.getOrderNo(), payOrder.getAmount().toString());
            } catch (Exception e) {
                e.printStackTrace();
                /**
                 * 做兜底
                 * 2.保存要取消支付单的记录到数据库，运维人员来人工跟进
                 * 3.给运维人员发站内信，邮件，消息---- 对接钉钉推销
                 */
            }
            return "success";
        }

        //订单状态是否为等待支付
        if(!(payOrder.getPayStatus()==PayOrder.STATE_WAIT_PAY)){
            return "Fail";
        }
        //回调的数据状态是否是成功支付的
        String trade_status = dto.getTrade_status();
        if(!trade_status.equals(AlipayNotifyDto.TRADE_SUCCESS)){
            return "Fail";
        }


        //修改支付单状态
        payOrder.setPayStatus(PayOrder.STATE_PAY_SUCCESS);
        payOrder.setUpdateTime(new Date());
        //payOrderService.updateById(payOrder);


        //mq发送事务广播消息   -----》添加courselearn表  和修改订单状态
        PayNotifyDto payNotifyDto = new PayNotifyDto();
        payNotifyDto.setOrderNo(dto.getOut_trade_no());
        payNotifyDto.setExtParams(payOrder.getExtParams());

        Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(payNotifyDto)).build();
        TransactionSendResult transactionSendResult = rocketMQTemplate.sendMessageInTransaction(
                "service-pay-to-course-order-producer",
                "pay-success-topic:course-order-saveorupdate-tags",
                message,
                payOrder
        );
        //发送状态
        SendStatus sendStatus = transactionSendResult.getSendStatus();

        boolean sendOK = sendStatus == SendStatus.SEND_OK;
        //AssertUtil.isTrue(sendOK,"下单失败");
        LocalTransactionState localTransactionState = transactionSendResult.getLocalTransactionState();
        boolean LocalOK = localTransactionState == LocalTransactionState.COMMIT_MESSAGE;
        //AssertUtil.isTrue(LocalOK,"消息发送失败");
        if(!sendOK || !LocalOK){
            throw new GlobalException("处理支付成功结果失败！！");
            // TODO 应该记录日志，然后通知相关人员 处理mq 发送消息失败
        }
        //删除redis中的预存订单
        KillPreOrderParamsDto payorder = (KillPreOrderParamsDto) redisTemplate.opsForValue().get(payOrder.getOrderNo());
        if(payorder!=null){
            log.info("进入了redis删除预存订单");
            redisTemplate.delete(payorder.getOrderNo());
        }

        return "success";
    }


    /**
     * 调用支付宝支付，返回Form表单
     * @return
     * @throws Exception
     */
    public String alipay(PayOrder payOrder,PayParametersDto dto){
        AlipayInfo info = alipayInfoService.selectOne(null);
        // 1. 设置参数（全局只需设置一次）
        Factory.setOptions(getOptions(info));
        try {
            // 2. 网页端支付
            Client page = Factory.Payment.Page();
            //封装支付相关参数，发起支付
            AlipayTradePagePayResponse response = page.pay(
                    payOrder.getSubject(),
                    payOrder.getOrderNo(),
                    payOrder.getAmount().toString(),
                    StringUtils.isEmpty(dto.getCallUrl())?info.getReturnUrl():dto.getCallUrl()//同步回调地址 支付成功跳转的页面
            );
            // 3. 处理响应或异常
            if (ResponseChecker.success(response)) {
                System.out.println("调用成功");
                return response.getBody();//Form表单
            } else {
                System.err.println("调用失败，原因：");
                throw new GlobalException("支付调用失败！！！");
            }
        } catch (Exception e) {
            System.err.println("调用遭遇异常，原因：" + e.getMessage());
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 设置访问参数
     * @param info
     * @return
     */
    public Config getOptions(AlipayInfo info) {
        Config config = new Config();
        config.protocol = info.getProtocol();
        config.gatewayHost = info.getGatewayHost();//沙箱API地址
        config.signType = info.getSignType();
        config.appId = info.getAppId();
        // 为避免私钥随源码泄露，推荐从文件中读取私钥字符串而不是写入源码中
        config.merchantPrivateKey = info.getMerchantPrivateKey();
        //注：如果采用非证书模式，则无需赋值上面的三个证书路径，改为赋值如下的支付宝公钥字符串即可
        config.alipayPublicKey = info.getAlipayPublicKey();
        //可设置异步通知接收服务地址（可选）
        config.notifyUrl = info.getNotifyUrl();
        //可设置AES密钥，调用AES加解密相关接口时需要（可选）
        config.encryptKey = "";
        return config;
    }
}
